package nachos.threads;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

import nachos.machine.*;

/**
 * A <i>communicator</i> allows threads to synchronously exchange 32-bit
 * messages. Multiple threads can be waiting to <i>speak</i>,
 * and multiple threads can be waiting to <i>listen</i>. But there should never
 * be a time when both a speaker and a listener are waiting, because the two
 * threads can be paired off at this point.
 */
public class Communicator {
	/**
	 * Allocate a new communicator.
	 */
	public Communicator() {
		theLock = new Lock();

		ghettoQueueIsFull = false;
		speakerWaiting = false;
		listenerWaiting = false;
		speakerWaitForListen = false;
		listenerWaitForSpeak = false;
		tillThereIsNoSpeaker = new Condition2(theLock);
		tillAListenerArrives = new Condition2(theLock);
		tillTheWordIsEmpty = new Condition2(theLock);
		tillASpeakerArrives = new Condition2(theLock);
		speakReady = new Condition2(theLock);
		listenReady = new Condition2(theLock);
		speaker = false;
		listener = false;
		
		messages = new ArrayList<Integer>();                
		myLock = new Lock();
		cond = new Condition2(myLock);
		cond2 = new Condition2(myLock);
		cond3 = new Condition2(myLock);
	}
	/*private Queue<KThread> listOfSpeakers = new LinkedList<KThread>();
	private Queue<Integer> derp = new LinkedList<Integer>(); //Queue<Integer> herp;
	private int ghettoQueue;
	private boolean ghettoQueueIsFull = false;
	private int numberOfSpeak = 0, numberOfListen = 0, waitingSpeaks = 0, waitingListens = 0;
	Lock lockFill = new Lock();
	Condition2 listenReady = new Condition2(lockFill);
	Condition2 speakReady = new Condition2(lockFill);*/
	private Lock theLock;
	private boolean ghettoQueueIsFull;
	private boolean thereIsASpeaker;
	private boolean speakerWaitForListen;
	private boolean listenerWaitForSpeak;
	private boolean thereIsNoListener;
	private boolean speakerWaiting;
	private boolean listenerWaiting;
	private boolean thereIsAListener;
	private boolean theWordIsFull;
	private boolean speaker;
	private boolean listener;
	private int theWord;
	private int ghettoQueue;
	private Condition2 tillThereIsNoSpeaker;
	private Condition2 tillAListenerArrives;
	private Condition2 tillTheWordIsEmpty;
	private Condition2 tillASpeakerArrives;
	private Condition2 speakReady;
	private Condition2 listenReady;
	private ArrayList<Integer> messages;
	private int numSpeaking = 0;
	private int numListening = 0;
	private Lock myLock;
	private Condition2 cond;
	private Condition2 cond2;
	private Condition2 cond3;

/*
	public void speak(int word){
		queue word
		if (listenReady.getQueue().nextThread() != null) {
			KThread fillerthread = listenReady.getQueue().nextThread();
			ImListening = true;
			fillerthread.this.listen();
			ImListening = false;
		}

	}

	public int listen(){
		if (ImListening == false && !queue.isEmpty()) {
			listenReady.sleep();
		}
		else {
			temp = queue.next();
			return temp;
		}
	}
*/
	/*
	public void speak(int word) {
		theLock.acquire();
		while(thereIsASpeaker) {
			tillThereIsNoSpeaker.sleep();
		}

		thereIsASpeaker = true;

		while(!thereIsAListener) {
			tillAListenerArrives.sleep();
		}

		theWord = word;
		theWordIsFull = true;
		while(theWordIsFull) {
			tillTheWordIsEmpty.sleep();
		}
		//signal speak is done

		thereIsASpeaker = false;
		theLock.release();
	}

	public int listen() {
		theLock.acquire();
		while(thereIsAListener) {
			tillThereIsNoListener.sleep();
		}
		thereIsAListener = true;
		while(!thereIsASpeaker){
			tillASpeakerArrives.sleep();
		}	
		int takenWord = theWord;
		theWordIsFull = false;

		tillTheWordIsEmpty.wake(); //signal word is taken		

		theLock.release();
		return takenWord;
	}

	 */
	
	public void speak(int word) {
	
		myLock.acquire();
		numSpeaking++;
		messages.add(word);
		
		if (numListening > 0)
			cond2.wake();
		cond.sleep();
		myLock.release();
		//System.out.println("speak done "+word);	
		
		/*
		System.out.println("SPEAK CALL:" + word);

		this.theLock.acquire();
		while(speaker) {//the speaker queue
			System.out.println("speak entered in queue.");
			speakerWaiting = true;
			speakReady.sleep();
		}
		speaker = true;
		if(!listener){//next in line
			System.out.println("Speak ready for listen.");
			speakerWaitForListen = true;
			speakReady.sleep();
			}
	//	if(wordCheck == false){
			this.ghettoQueue = word;//}//get the word
		System.out.println("SPEAK VAL:" + word);
		//wordCheck = true;
		speaker = false; listener = false;
		if (listenerWaitForSpeak == true) {//If there's a listen waiting (listen is false), call it here
			listenerWaitForSpeak = false;
			System.out.println("speak waking up listen.");
			listenReady.wake();

		}
		if (listenerWaiting == true) {//If there's listens in the queue, shuffle forward
			System.out.println("speak shuffling listen queue.");

			listenerWaiting = false;
			this.listenReady.wakeAll();
		}
		this.theLock.release();
*/
	}

	public int listen() {
		
		myLock.acquire();	
		int retval = 0;
		numListening++;
		//System.out.println("$"+numListening);
		
		if (numSpeaking == 0)
			cond2.sleep();
		if (numSpeaking > 0) {
			numSpeaking--;
			retval = messages.get(0);
			messages.remove(0);
			cond.wake();
			numListening--;
		}
		
		myLock.release();
		//System.out.println("listen done "+retval);
		return retval;
		
		/*
		System.out.println("LISTEN CALL.");
		this.theLock.acquire();

		while(listener) {//while there is a listener
			System.out.println("listen entered in queue.");
			listenerWaiting = true;
			listenReady.sleep();

		}
		listener = true;

		if(speakerWaiting == true){
			speakReady.wake();
			speakerWaiting = false;
		}
		if(!speaker) { //while there is no speaker
			listenerWaitForSpeak = true;
			System.out.println("listen waiting for speak.");
			listenReady.sleep();
		}
		if (speakerWaitForListen == true) {
			System.out.println("listen waking up speak.");

			speakerWaitForListen = false;
			speakReady.wake();
		}
		int toReturn = this.ghettoQueue;
		System.out.println("LISTEN VAL:" + toReturn);
		speaker = false; //wordCheck = false;
		listener = false;
		if (speakerWaiting == true) {
			System.out.println("listen shuffling speak queue.");
			speakerWaiting = false;
			speakReady.wakeAll();
		}
		this.theLock.release();

		return toReturn;*/
	}

	/**
	 * Wait for a thread to listen through this communicator, and then transfer
	 * <i>word</i> to the listener.
	 *
	 * <p>
	 * Does not return until this thread is paired up with a listening thread.
	 * Exactly one listener should receive <i>word</i>.
	 *
	 * @param	word	the integer to transfer.
	 */

	/*public void speak(int word) {
		lockFill.acquire();
		if(numberOfSpeak > 1){
			System.out.println("sleeping speak - waiting speaks : " + ++waitingSpeaks + " waiting Listens : " + waitingListens);
			speakReady.sleep();
			System.out.println("waking speak - waiting speaks : " + --waitingSpeaks + " waiting Listens : " + waitingListens);
		}
			numberOfSpeak++;


			numberOfListen--;
			listenReady.wake();
			//System.out.println("WILL FINALLY STOP SPEAKING! with my word:" + word);

		lockFill.release();
	}

	/**
	 * Wait for a thread to speak through this communicator, and then return
	 * the <i>word</i> that thread passed to <tt>speak()</tt>.
	 *
	 * @return	the integer transferred.
	 *
	 *
	 *	
		while(ghettoQueueIsFull) {
			System.out.println("I AM TRYING TO SPEAK AND WILL SLEEP" + word);
			lockFill.acquire();
			speakReady.sleep();
			System.out.println("I AM NOW AWAKE AND GETTING READY TO SPEAK " + word);
		}
		System.out.println("I AM SPEAKING " + word);
		ghettoQueue = word;
		ghettoQueueIsFull = true;
		System.out.println("I AM SPEAKING but will sleep " + word);
		lockFill.acquire();
		speakReady.sleep();
		System.out.println("I AM NOW AWAKE AND WILL SPEAK " + word);
		listenReady.wake();
		//lockFill.release();
	 *		
		//lockFill.acquire();
		/*System.out.println("NEWWWWWW LISTEN!!!!!!!!!!!");
		while(!ghettoQueueIsFull) {
			System.out.println("I AM LISTENING but fell asleep ");
			lockFill.acquire();
			listenReady.sleep();
			System.out.println("I AM NOW AWAKE TO LISTEN");
			speakReady.wake();
		}
		int temp = ghettoQueue;
		lockFill.acquire();
		System.out.println("I AM LISTENING " + ghettoQueue);
		ghettoQueueIsFull = false;
		speakReady.wake();
		lockFill.release();

	 **/    
	/*public int listen() {
		lockFill.acquire();
		//System.out.println("NEWWWWWW LISTEN!!!");
		numberOfListen++;
		if(numberOfSpeak == 0 && numberOfListen != 0){
			System.out.println("sleeping listen -waiting speaks : " + waitingSpeaks + " waiting listens : " + ++waitingListens);
			listenReady.sleep();
			System.out.println("waking listen - waiting speaks : " + waitingSpeaks + " waiting Listens : " + --waitingListens);
		}
		numberOfSpeak--;
		int temp = ghettoQueue;
		//System.out.println("WHAT IS TEMP? " + temp);
		speakReady.wake();
		//System.out.println("WILL FINALLY EXIT WITH THE VALUE " + temp);
		lockFill.release();
		return temp;
	}*/
}
